package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.CreateActivityParam;
import com.example.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.example.lotterysystem.controller.param.CreateUserByActivityParam;
import com.example.lotterysystem.controller.param.PageParam;
import com.example.lotterysystem.dao.dataobject.*;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.ActivityService;
import com.example.lotterysystem.service.dto.ActivityDTO;
import com.example.lotterysystem.service.dto.ActivityDetailDTO;
import com.example.lotterysystem.service.dto.CreateActivityDTO;
import com.example.lotterysystem.service.dto.PageListDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 * Description: 创建活动接口的实现类
 * User: Spider-Man
 * Date: 2025-09-11
 * Time: 21:16
 */
@Service
public class ActivityServiceImpl implements ActivityService {

    //日志配置
    private static final Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);

    //key前缀
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    //过期时间
    private final long ACTIVITY_TIMEOUT = 60*60*24*3;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 创建活动
     * @param param
     * @return
     * (任何一个存储出现异常,我们需要回滚事务)--->需要使用注解:@Transactional
     *
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验活动是否正确
        checkActivityInfo(param); //校验出现错误,直接抛出异常

        //保存活动基本信息到活动表当中
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());//刚创建完活动应为-->> 活动进行中
        activityMapper.insert(activityDO);
        //保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = prizeParams.stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //保存活动关联的人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = userParams.stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);
        //整合活动, 活动关联的人员,活动关联的奖品到redis当中.
        //ActivityDetailDTO: 活动+奖品+人员 通过活动ID查询完整的信息

        //1.先获取到奖品的基本属性列表 (需要先获取到奖品id)
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam :: getPrizeId)
                .distinct()//去重
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDO,activityUserDOList,prizeDOList,activityPrizeDOList);
        cacheActivity(detailDTO); //缓存活动完整信息
        //构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;

    }


    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //获取活动总量
        int total = activityMapper.count();

        //获取当前页列表
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.offset(),param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total,activityDTOList);
    }

    /**
     * 获取活动信息
     * @param activityId
     * @return
     */
    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        //1.在redis当中查询活动信息
        if(activityId == null) {
            logger.warn("查询活动详细信息的activityId为空!");
            return null;
        }

        //redis当中存在这个数据直接返回
        ActivityDetailDTO detailDTO = getActivityFromCache(activityId);
        if(detailDTO != null) {
            logger.info("查询活动详细信息成功! detailDTO={}",
                    JacksonUtil.writeValueAsString(detailDTO));
            return detailDTO;
        }
        //(为从redis当中查询到数据)从数据库当中查询
        // 活动表
        ActivityDO aDO = activityMapper.selectById(activityId);
        // 活动奖品关联表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);
        // 活动人员关联表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        // 奖品表:先获取到奖品id
        List<Long> prizeIds = apDOList.stream().map(ActivityPrizeDO :: getPrizeId).collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        // 整合数据,再存放redis当中
        detailDTO = convertToActivityDetailDTO(aDO,auDOList,pDOList,apDOList);
        cacheActivity(detailDTO);//缓存数据
        //返回数据
        return detailDTO;
    }

    /**
     * 更新缓存消息
     * @param activityId
     */
    @Override
    public void cacheActivity(Long activityId) {
        if(activityId == null) {
            logger.warn("要缓存的活动id为空!");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //1.查询表数据(活动, 关联奖品,关联人员表,奖品信息表)
        ActivityDO aDO = activityMapper.selectById(activityId);
        if(aDO == null) {
            logger.error("要缓存的活动id有误!");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        // 活动奖品关联表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);
        // 活动人员关联表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        // 奖品表:先获取到奖品id
        List<Long> prizeIds = apDOList.stream().map(ActivityPrizeDO :: getPrizeId).collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        //2. 整合数据,再存放redis当中
        cacheActivity(convertToActivityDetailDTO(aDO,auDOList,pDOList,apDOList));
    }

    //缓存完整的活动信息
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        //key:ACTIVITY_12
        //value:ActivityDetailDTO(json)
        if(detailDTO == null || detailDTO.getActivityId() == null) {
            logger.warn("缓存的活动信息不存在!!!");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception e) {
            logger.error("缓存活动异常:{}",JacksonUtil.writeValueAsString(detailDTO),e);
        }
    }
    //从缓存当中获取数据 (根据活动Id获取数据)
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if(activityId == null) {
            logger.warn("获取活动数据的activityId为空!");
            return null;
        }
        try {
            String str = redisUtil.get(ACTIVITY_PREFIX+activityId);//如果查看失败了的话,我们就去Mysql当中获取数据
            //str可能为空,在进行反序列化的时候可能出现异常
            if(!StringUtils.hasText(str)) {
                logger.info("获取缓存活动数据为空! key={}",ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtil.readValue(str, ActivityDetailDTO.class);//将从缓存当中获取到的数据进行反序列化

        }catch(Exception e) {
            logger.error("从缓存当中获取活动信息异常, key={}",activityId,e);
            return null;
        }

    }

    //整合数据
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                            List<ActivityUserDO> activityUserDOList,
                                            List<PrizeDO> prizeDOList,
                                            List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
        //奖品信息列表 apDO: prizeId,amount,status
        //pDo:prizeId
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList.stream()
                        .map(apDO -> {
                            ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                            prizeDTO.setPrizeId(apDO.getPrizeId());
                            //奖品信息返回的是一个链表,需要进行遍历
                            //如果PrizeDO为空,不执行当前方法,不为空才执行, Optional解决空指针异常的
                            Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream().filter(prizeDO ->
                                prizeDO.getId().equals(apDO.getPrizeId()))
                                            .findFirst();
                            optionalPrizeDO.ifPresent(prizeDO -> {
                                prizeDTO.setName(prizeDO.getName());
                                prizeDTO.setImageUrl(prizeDO.getImageUrl());
                                prizeDTO.setPrice(prizeDO.getPrice());
                                prizeDTO.setDescription(prizeDO.getDescription());
                            });

                            prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                            prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                            prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                            return prizeDTO;
                        }).collect(Collectors.toList());
        detailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream()
                        .map(auDO ->{
                            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                            userDTO.setUserId(auDO.getUserId());
                            userDTO.setUserName(auDO.getUserName());
                            userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                            return userDTO;
                        }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);
        return detailDTO;
    }

    /**
     * 校验活动有效性
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if(param == null) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }
        //1.人员id在人员表中存在
        List<Long> userIds = param.getActivityUserList().stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct() //去重
                .collect(Collectors.toList()); //构造成为list
        List<Long> existUserIds = userMapper.selectExistByIds(userIds); //人员表中的所有id信息.
        if(CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if(!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });
        //2.奖品id在奖品表中存在
        List<Long> priceIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPriceIds = prizeMapper.selectExistByPriceIds(priceIds);
        if(CollectionUtils.isEmpty(existPriceIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRICE_ERROR);
        }
        priceIds.forEach(id -> {
            if(!existPriceIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRICE_ERROR);
            }
        });
        //3.奖品数量必须大于人员数量
        Integer userAmount = param.getActivityUserList().size();
        long priceAmount = param.getActivityPrizeList().stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();//对mapToLong获取到的数据进行加
        if(userAmount < priceAmount) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRICE_AMOUNT_ERROR);
        }

        //校验奖品等级是否有效
        param.getActivityPrizeList().forEach(prize -> {
            if(null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }
}
